package semant.second_pass;

import util.List;
import semant.Env;
import symbol.Symbol;
import symbol.ClassInfo;
import symbol.MethodInfo;
import symbol.VarInfo;
import syntaxtree.MethodDecl;
import syntaxtree.VarDecl;
import syntaxtree.Formal;
import syntaxtree.Statement;
import syntaxtree.Type;
import syntaxtree.VisitorAdapter;

final class MethodDeclHandler extends VisitorAdapter {

    private Env env;
    private ClassInfo cinfo;

    private MethodDeclHandler(Env e, ClassInfo c){
	super();
	env = e;
	cinfo = c;
    }

    static void secondPass(Env e, ClassInfo c, MethodDecl md){
	MethodDeclHandler h = new MethodDeclHandler(e, c);
	md.accept(h);
    }

    public void visit(MethodDecl node){
	Symbol mname = Symbol.symbol(node.name.s);
	MethodInfo minfo = cinfo.methods.get(mname);

	// verifica se o metodo esta sendo sobrescrito
	if(cinfo.base != null){
	    MethodInfo minfoSuper = cinfo.base.methods.get(mname);
	    if(minfoSuper != null){

		// tipos de retorno devem ser iguais
		if(!TypeHandler.equal(minfoSuper.type, minfo.type))
		    env.err.Error(node,
				  new Object[]{"Tipo invalido para retorno do metodo sobrescrito",
					       "Esperado: " + minfoSuper.type,
					       "Encontrado: " + minfo.type});

		// parametros devem ser iguais
		List<VarInfo> vic = minfo.formals, vis = minfoSuper.formals;
		int c = 1;
		while(vic != null && vis != null){
		    if(!TypeHandler.equal(vic.head.type, vis.head.type))
			env.err.Error(vic.head.type,
				      new Object[]{"Tipo invalido para parametro #" + c + " do metodo sobrescrito",
						   "Esperado: " + vis.head.type,
						   "Encontrado: " + vic.head.type});

		    vic = vic.tail;
		    vis = vis.tail;
		    c++;
		}

		// numero de parametros dos metodos devem ser iguais
		if(vic != null || vis != null)
		    env.err.Error(node, new Object[]{"Numero invalido de parametros para metodo sobrescrito"});
	    }
	}

	// verifica formals
	for(List<Formal> f = node.formals; f != null; f = f.tail)
	    FormalHandler.secondPass(env, f.head);

	// verifica locals
	for(List<VarDecl> l = node.locals; l != null; l = l.tail)
	    VarDeclHandler.secondPass(env, l.head);

	// verifica statements no corpo do metodo
	for(List<Statement> s = node.body; s != null; s = s.tail)
	    StatementHandler.secondPass(env, cinfo, minfo, s.head);

	// verifica tipo de retorno
	TypeHandler.secondPass(env, node.returnType);

	// verifica expressao de retorno
	Type ret = ExpHandler.secondPass(env, cinfo, minfo, node.returnExp);
	if(!TypeHandler.compatible(env, node.returnType, ret)){
	    env.err.Error(node.returnExp,
			  new Object[]{"Tipo invalido para retorno do metodo",
				       "Esperado: " + node.returnType,
				       "Encontrado: " + ret});
	}
    }

}
